home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
L' Effet Pommier 3
/
L'Effet Pommier - Volume 03.iso
/
Programmation
/
gray image 2.1
/
write_tiff.cc
< prev
next >
Wrap
Text File
|
1995-05-30
|
13KB
|
404 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Grayscale Image
*
* Write an image into a file in the TIFF format
*
* The present TIFF writer creates a Class G TIFF file (for gray-scale
* images), See Appendix G of the TIFF specification.
*
* The program writes the following required tags
* NewSubfileType
* ImageWidth
* ImageLength
* RowsPerStrip (To make strips around 8K)
* StripOffsets
* StripByteCounts
* XResolution (72 dpi)
* YResolution (72 dpi)
* ResolutionUnit
*
* In addition, the class G TIFF files are required to have the following
* tags with the following values
* SamplesPerPixel = 1
* BitsPerSample = 4 or 8
* Compression = 1 or 5(LZW)
* PhotometricInterpretation = 0 or 1
*
* In addition, if the image has name, it's written under
* ImageDescription,
* The title, if specified, is written as
* DocumentName
* tag
*
* $Id: write_tiff.cc,v 2.0 1995/03/06 21:55:06 oleg Exp oleg $
*
************************************************************************
*/
#include "image.h"
#include "endian_io.h"
#pragma implementation "tiff.h"
#include "tiff.h"
// Write a TIFF header into the file
void TIFFHeader::write(EndianOut& file)
{
if( magic == TIFF_BIGENDIAN )
file.set_bigendian();
else if( magic == TIFF_LITTLEENDIAN )
file.set_littlendian();
else
_error("Unknown magic word %x",magic);
file.write_short(magic);
file.write_short(version);
file.write_long(diroffset);
}
/*
*------------------------------------------------------------------------
* TIFF Directory
* Note that all items in the TIFF directory are threaded in an ascending
* order of their tags. So, when a new DirectoryItem is created, it inserts
* itself into the thread at proper position.
* Note, if some directory item has count field equal 0, the item is
* considered "dummy" and it's not written to the TIFF directory in the file.
*
*/
class TIFFDirectoryItem : public TIFFDirEntry
{
static int no_entries; // So all TIFFDirEntry's got threaded
static TIFFDirectoryItem * entries[250]; // through in an ascending order
protected:
virtual void write(EndianOut& file) = 0; // Write this field into a file
virtual void write_value(EndianOut& file) = 0;// Write additional data
public:
TIFFDirectoryItem(const short _tag, const DataType _type, const long _count,
const long _value);
void write_all(EndianOut& file); // Write the entire TIFF directory
};
int TIFFDirectoryItem::no_entries = 0;
TIFFDirectoryItem * TIFFDirectoryItem::entries[250];
// Insert an item into the directory
// in an ascending order of its tag
TIFFDirectoryItem::TIFFDirectoryItem
(const short _tag, const DataType _type, const long _count,
const long _value)
: TIFFDirEntry(_tag,_type,_count,_value)
{
if( no_entries >= sizeof(entries)/sizeof(entries[0]) )
_error("Can't add a new entry, the directory is full, %d items",
no_entries);
register int pos; // Find a position where the present
for(pos=0; pos<no_entries; pos++) // item belongs (according to its tag)
if( entries[pos]->tag > tag )
break;
register int i; // Move items with bigger tags toward
for(i=no_entries-1; i >= pos; i--) // the end of the array to make space
entries[i+1] = entries[i]; // for our item
entries[pos] = this;
no_entries++;
}
// Write the entire directory and clean up
void TIFFDirectoryItem::write_all(EndianOut& file)
{
int no_nondummy_items = 0;
register int i;
for(i=0; i<no_entries; i++) // Count non-dummy dir entries
if( entries[i]->count != 0 )
no_nondummy_items++;
assert( no_nondummy_items > 0 );
file.write_short(no_nondummy_items);
for(i=0; i<no_entries; i++)
if( entries[i]->count != 0 )
entries[i]->write(file);
file.write_long(0); // Specify this is the end of the IFD
// no other IFD is to follow
for(i=0; i<no_entries; i++) // Handle items that store their values
if( entries[i]->count != 0 ) // after the directory
entries[i]->write_value(file);
no_entries = 0;
}
/*
*------------------------------------------------------------------------
* Particular types of DirectoryItems
*/
// Scalar item. It keeps its value in the
// val_offset field of the DirItem
// Note, values shorter than long are
// LEFT-justified
class ScalarTIFFDE : public TIFFDirectoryItem
{
protected:
void write(EndianOut& file); // Write this field into a file
void write_value(EndianOut& file) {} // Value is a part of the item, and
// is written along with the item
public:
ScalarTIFFDE(const short _tag, const TSHORT _value)
: TIFFDirectoryItem(_tag,SHORT,1,(unsigned long)_value << 16) {}
ScalarTIFFDE(const short _tag, const TLONG _value)
: TIFFDirectoryItem(_tag,LONG,1,_value) {}
};
// Write this item into a file
void ScalarTIFFDE::write(EndianOut& file)
{
file.write_short(tag);
file.write_short(type);
file.write_long(count);
file.write_long(val_offset);
}
// String item, the string itself is written
// after the IFD, the val_offset field of
// the item stores an offset to the string
// characters.
class StringTIFFDE : public TIFFDirectoryItem
{
protected:
void write(EndianOut& file); // Write this field into a file
void write_value(EndianOut& file); // Write the body of the string
// (after the directory is written)
streampos offset_for_val; // for the val_offset field
public:
StringTIFFDE(const short _tag, const char * str)
: TIFFDirectoryItem(_tag,ASCII, str == 0 || str[0] == '\0' ? //empty string
0 : strlen(str)+1,(long)str) {}
};
// Write this item into a file
void StringTIFFDE::write(EndianOut& file)
{
assert( count > 0 );
file.write_short(tag);
file.write_short(type);
file.write_long(count);
offset_for_val = file.tellp();
file.write_long(0); // write a dummy for now
}
// Write the body of the string,
// Note, a padding may be necessary to
// make the string end at a word boundary
void StringTIFFDE::write_value(EndianOut& file)
{
streamoff pos = file.tellp(); // where are we now
// Write a body of the string, '\0'
// and (possible) an extra byte
file.write((char *)val_offset,(strlen((char *)val_offset)+1+1) & ~1);
// to keep word-boundary align
streampos pos_new = file.tellp();
assert( file.seekp(offset_for_val).good() );
file.write_long(pos); // Write the offset within the dir elem
assert( file.seekp(pos_new).good() ); // Restore the file position
// to continue writing
}
// Directory item that refers to an array
// (that can contain strip offsets, strip
// byte counts, or other info specific to
// each strip)
class ArrayTIFFDE : public TIFFDirectoryItem
{
protected:
virtual void write(EndianOut& file); // Write this field into a file
virtual void write_value(EndianOut& file); // Write the body of the array
// (after the directory is written)
streampos offset_for_val; // for the val_offset field
long * array;
public:
ArrayTIFFDE(const short _tag, const long no_elems);
virtual ~ArrayTIFFDE(void);
long& operator [] (const int index) // Get hold of an element of the array
{ assert( index >= 0 && index < count ); return array[index]; }
};
// Constructor - allocate the array
ArrayTIFFDE::ArrayTIFFDE(const short _tag, const long no_elems)
: TIFFDirectoryItem(_tag, LONG, no_elems, 0)
{
assert( count > 0 );
array = new long[count];
}
// Dispose of the array
ArrayTIFFDE::~ArrayTIFFDE(void)
{
assert( array != 0 );
delete [] array;
array = 0;
}
// Write this item into a file
void ArrayTIFFDE::write(EndianOut& file)
{
assert( count > 0 );
file.write_short(tag);
file.write_short(type);
file.write_long(count);
offset_for_val = file.tellp();
file.write_long(0); // write a dummy for now
}
// Write array elements (if more than 1)
// after the directory
void ArrayTIFFDE::write_value(EndianOut& file)
{
long int pos; // Where the array is to be written
register int i;
if( count == 1 ) // If there is only one elem, keep
pos = array[0]; // the offset with the dir elem itself
else
{
pos = file.tellp(); // Write the array separately
for(i=0; i<count; i++)
file.write_long(array[i]);
}
streampos pos_new = file.tellp();
assert( offset_for_val != 0 && file.seekp(offset_for_val).good() );
file.write_long(pos); // Write the offset within the dir elem
assert( file.seekp(pos_new).good() ); // Restore the file position
// to continue writing
}
// Rational number: an array of two LONGs
class RationalTIFFDE : public ArrayTIFFDE
{
virtual void write(EndianOut& file); // Write this field into a file
public:
RationalTIFFDE(const short _tag, const int numerator,const int denominator)
: ArrayTIFFDE(_tag,2)
{ array[0] = numerator; array[1] = denominator; type = RATIONAL; }
};
// Write this item into a file: only one
// modification: a rational has count 1, though
// it's an array of 2 longs
void RationalTIFFDE::write(EndianOut& file)
{
assert( count == 2 );
count = 1; // temporarily: that's what we write
ArrayTIFFDE::write(file); // into the tag
count = 2;
}
// Strip item, that refers to an array
// that contains offsets to the strips (set
// of rows) of the image
// No compression is used at present
class StripTIFFDE : public ArrayTIFFDE
{
protected:
void write_value(EndianOut& file); // Write the array of strip ptrs
// and strips themselves
// (after the directory is written)
ArrayTIFFDE strip_byte_counts; // It is not necessary unless compress-
// ion is used; but neverthelesss
short rows_per_strip;
const IMAGE& image;
public:
StripTIFFDE(const short _no_strips, const short _rows_per_strip,
const IMAGE& _image)
: ArrayTIFFDE(TIFFTAG_STRIPOFFSETS,_no_strips),
strip_byte_counts(TIFFTAG_STRIPBYTECOUNTS,_no_strips),
rows_per_strip(_rows_per_strip), image(_image) {}
};
// Write the array of strip ptrs
// and strips themselves
// (after the directory is written)
// Note the trick! StripByteCount
// has a bigger tag, i.e. strip_byte_
// count will be written AFTER the
// strips are written, i.e., when
// strip sizes are already known
void StripTIFFDE::write_value(EndianOut& file)
{
assert( count > 0 );
register int strip;
for(strip=0; strip<count; strip++)
{
array[strip] = file.tellp(); // where are we now, beg of a strip
register int i,j; // Writing a strip
for(i=strip*rows_per_strip;
i<(strip+1)*rows_per_strip && i < image.q_nrows(); i++)
for(j=0; j<image.q_ncols(); j++)
file.write_byte(image(i,j));
strip_byte_counts[strip] = file.tellp() - array[strip];
}
ArrayTIFFDE::write_value(file);
}
/*
*------------------------------------------------------------------------
* Root module
*/
void IMAGE::write_tiff(const char * file_name,const char * title) const
{
is_valid();
message("\nPreparing a TIFF file with name '%s'\n",file_name);
EndianOut file(file_name);
TIFFHeader header;
header.write(file);
ScalarTIFFDE subfile_tag(TIFFTAG_SUBFILETYPE,(unsigned)0);
ScalarTIFFDE width_tag(TIFFTAG_IMAGEWIDTH,(unsigned)q_ncols());
ScalarTIFFDE length_tag(TIFFTAG_IMAGELENGTH,(unsigned)q_nrows());
ScalarTIFFDE compr_tag(TIFFTAG_COMPRESSION,(unsigned short)COMPRESSION_NONE);
ScalarTIFFDE phot_tag(TIFFTAG_PHOTOMETRIC,
(unsigned short)PHOTOMETRIC_MINISBLACK);
ScalarTIFFDE resunit_tag(TIFFTAG_RESOLUTIONUNIT,
(unsigned short)RESUNIT_INCH);
// Means 72 pixels per unit (inch)
RationalTIFFDE xres_tag(TIFFTAG_XRESOLUTION,72,1);
RationalTIFFDE yres_tag(TIFFTAG_YRESOLUTION,72,1);
ScalarTIFFDE samplesize_tag(TIFFTAG_SAMPLESPERPIXEL,(unsigned short)1);
assert( bits_per_pixel == 8 );
ScalarTIFFDE depth_tag(TIFFTAG_BITSPERSAMPLE,(unsigned short)8);
StringTIFFDE desc_tag(TIFFTAG_IMAGEDESCRIPTION, name );
StringTIFFDE docname_tag(TIFFTAG_DOCUMENTNAME, title );
const int strip_target_size = 8*1024;
unsigned int rows_per_strip = strip_target_size / q_ncols();
int no_strips = (q_nrows() + rows_per_strip - 1)/rows_per_strip;
ScalarTIFFDE striprs_tag(TIFFTAG_ROWSPERSTRIP,rows_per_strip);
StripTIFFDE stripoffs_tag(no_strips, rows_per_strip, *this);
subfile_tag.write_all(file);
file.close();
}